package com.sf.fy.uav.applet.utils;

import com.alibaba.fastjson.JSONObject;

import java.math.BigDecimal;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
 * 根据 longitude + latitude + distance 计算 minLat maxLat minLng maxLng
 * <p>
 * 计算两点位置的距离
 *
 * @author 80003124 张德强
 * @time 2018年5月15日 下午3:31:49
 */
public class DistanceHelper {

    /**
     * 赤道半径 KM
     */
    private static final double r = 6378.137;

    /**
     * 根据角度(经度、纬度)转换为弧度
     *
     * @param angleDegree
     * @return
     */
    public static double rad(double angleDegree) {
        return (double) angleDegree * Math.PI / 180;
    }

    /**
     * 根据坐标计算指定距离的最大最小经纬度
     *
     * @param longitude
     * @param latitude
     * @param distance
     * @return
     */
    public static Map<String, Object> findNeighPosition(double longitude, double latitude, double distance) {
        // 先计算查询点的经纬度范围
        double dis = distance;// 千米距离
        double dlng = 2 * Math.asin(Math.sin(dis / (2 * r)) / Math.cos(latitude * Math.PI / 180));
        dlng = dlng * 180 / Math.PI;// 弧度转为角度
        double dlat = dis / r;
        dlat = dlat * 180 / Math.PI;
        double minlat = latitude - dlat;// 最小纬度
        double maxlat = latitude + dlat;// 最大纬度
        double minlng = longitude - dlng;// 最小经度
        double maxlng = longitude + dlng;// 最大经度

        Map<String, Object> map = new HashMap<String, Object>();
        map.put("minlat", minlat);
        map.put("maxlat", maxlat);
        map.put("minlng", minlng);
        map.put("maxlng", maxlng);
        return map;

    }

    /**
     * 判断一个点是否在圆形区域内，单位米
     */
    public static boolean isInCircle(double lon, double lat, double circleLon, double circleLat, String circleRadius) {
        return getDistance(lat, lon, circleLat, circleLon) < Double.parseDouble(circleRadius);
    }

    /**
     * 计算两点之间的直线距离 单位km
     *
     * @param srcLat  A点纬度
     * @param srcLng  A点经度
     * @param destLat B点纬度
     * @param destLng B点经度
     * @return
     */
    public static Double getDistance(double srcLat, double srcLng, double destLat, double destLng) {
        // 地球半径，单位米
        double EARTH_RADIUS = r;
        double srcRadLat = rad(srcLat);
        double srcRadLng = rad(srcLng);
        double destRadLat = rad(destLat);
        double destRadLng = rad(destLng);
        double a = srcRadLat - destRadLat;
        double b = srcRadLng - destRadLng;
        double result = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2)
                + Math.cos(srcRadLat) * Math.cos(destRadLat) * Math.pow(Math.sin(b / 2), 2))) * EARTH_RADIUS;
        return new BigDecimal(result).setScale(6, BigDecimal.ROUND_HALF_UP).doubleValue();
    }

    /**
     * 计算两点之间的直线距离 单位m
     *
     * @param srcLat  A点纬度
     * @param srcLng  A点经度
     * @param destLat B点纬度
     * @param destLng B点经度
     * @return
     */
    public static Double getDistanceM(double srcLat, double srcLng, double destLat, double destLng) {
        // 地球半径，单位米
        double EARTH_RADIUS = r * 1000;
        double srcRadLat = rad(srcLat);
        double srcRadLng = rad(srcLng);
        double destRadLat = rad(destLat);
        double destRadLng = rad(destLng);
        double a = srcRadLat - destRadLat;
        double b = srcRadLng - destRadLng;
        double result = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2)
                + Math.cos(srcRadLat) * Math.cos(destRadLat) * Math.pow(Math.sin(b / 2), 2))) * EARTH_RADIUS;
        return new BigDecimal(result).setScale(3, BigDecimal.ROUND_HALF_UP).doubleValue();
    }

    /**
     * 计算两点之间的直线距离 单位m
     *
     * @param lat1 A点纬度
     * @param lng1 A点经度
     * @param lat2 B点纬度
     * @param lng2 B点经度
     * @return
     */
    public static Double getDistanceM(String lat1, String lng1, String lat2, String lng2) {
        return getDistanceM(Double.parseDouble(lat1), Double.parseDouble(lng1), Double.parseDouble(lat2), Double.parseDouble(lng2));
    }


    /**
     * 计算爬升角
     *
     * @return
     */
    public static Double getClimbingAngle(double distance, double height) {
        return new BigDecimal(height).divide(new BigDecimal(distance), 3, BigDecimal.ROUND_HALF_UP).doubleValue();
    }

    /**
     * 计算三个点连线的夹角
     *
     * @param startLat
     * @param startLng
     * @param middleLat
     * @param middleLng
     * @param endLat
     * @param endLng
     * @return
     */
    public static Double getAngleOfThreePoint(double startLat, double startLon, double middleLat, double middleLon,
                                              double endLat, double endLon) {
        double ma_x = rad(startLat) - rad(middleLat);
        double ma_y = rad(startLon) - rad(middleLon);
        double mb_x = rad(endLat) - rad(middleLat);
        double mb_y = rad(endLon) - rad(middleLon);
        double v1 = (ma_x * mb_x) + (ma_y * mb_y);
        double ma_val = Math.sqrt(ma_x * ma_x + ma_y * ma_y);
        double mb_val = Math.sqrt(mb_x * mb_x + mb_y * mb_y);
        double cosM = v1 / (ma_val * mb_val);
        double angleAMB = Math.acos(cosM) * 180 / Math.PI;
        return angleAMB;
    }

    /**
     * 求点到线段的最小距离
     *
     * @param pointLat
     * @param pointLon
     * @param lineStartLat
     * @param lineStartLon
     * @param lineEndLat
     * @param lineEndLon
     * @return
     */
    public static Double getPointToLineDistance(double pointLat, double pointLon, double lineStartLat, double lineStartLon,
                                                double lineEndLat, double lineEndLon) {
        Double a = getDistance(lineStartLat, lineStartLon, lineEndLat, lineEndLon);
        Double b = getDistance(lineEndLat, lineEndLon, pointLat, pointLon);
        Double c = getDistance(pointLat, pointLon, lineStartLat, lineStartLon);
        if (a * a + c * c <= b * b) {
            return c;
        }
        if (a * a + b * b <= c * c) {
            return b;
        }
        double l = (a + b + c) / 2;
        double s = Math.sqrt(l * (l - a) * (l - b) * (l - c));
        return 2 * s / a;
    }


    /**
     * 根据参考经纬点，距离、方位计算移动后的经纬度
     * 备注：
     * 1. 正北方向是0弧度，即 0度 * (360 * Math.PI / 2) = 0弧度
     * 2.
     * 参考：https://blog.csdn.net/sinat_32857543/article/details/107207553
     *
     * @param lon 经度
     * @param lat 纬度
     * @param a   方位角（弧度）
     * @param dst 移动距离(米)
     * @return
     */
    public static double[] longLatOffset(double lon, double lat, double a, double dst) {
        double arc = r * 1000;
        lon += dst * Math.sin(a) / (arc * Math.cos(rad(lat)) * 2 * Math.PI / 360);
        lat += dst * Math.cos(a) / (arc * 2 * Math.PI / 360);
        return new double[]{lon, lat};
    }


    public static Map<String, Object> getRectangle(double lon, double lat, double distance) {
        double[] p1 = longLatOffset(lon, lat, rad(315), distance);
        double[] p2 = longLatOffset(lon, lat, rad(45), distance);
        double[] p3 = longLatOffset(lon, lat, rad(135), distance);
        double[] p4 = longLatOffset(lon, lat, rad(225), distance);

        Map<String, Object> map = new HashMap<String, Object>(16);
        map.put("minLat", Arrays.asList(p1[1], p2[1], p3[1], p4[1]).parallelStream().min(Double::compare).get());
        map.put("maxLat", Arrays.asList(p1[1], p2[1], p3[1], p4[1]).parallelStream().max(Double::compare).get());
        map.put("minLon", Arrays.asList(p1[0], p2[0], p3[0], p4[0]).parallelStream().min(Double::compare).get());
        map.put("maxLon", Arrays.asList(p1[0], p2[0], p3[0], p4[0]).parallelStream().max(Double::compare).get());
        return map;
    }


    public static void main(String[] args) {
        System.out.println(getRectangle(113.9356540, 22.6901570, 12));
        Map<String, Object> data = findNeighPosition(10.52, 16.23, 50);
        System.out.println(JSONObject.toJSONString(data));
        Double d = getClimbingAngle(5.0, 3.0);
        System.out.println(d);
        System.out.println("两点之间的直线距离 单位km:" + getDistance(22.5360282, 113.9258673, 22.5361372, 113.9234426));
        //System.out.println(getPointToLineDistance(26.0534, 114.686, 26.055577, 114.69993, 26.080715, 114.672015));
    }
}
